//
// Created by meow star on 2019/10/3.
//

#include <thread>
#include <chrono>
#include <unordered_map>
#include <string>
#include <unistd.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
#include "application.h"
#include "stdio.h"
#include "stdlib.h"
using namespace std::chrono;
void Application::build_json_from_recall(RecallResult &result, json &j) {
    for(auto it_iindex_type = result.results.begin(); it_iindex_type != result.results.end(); ++ it_iindex_type){
        const string& iindex_type = it_iindex_type->first;
        vector<IIndexRecallResult>& iindex_results = it_iindex_type->second;
        int order = 0;
        for(auto it_iindex_result = iindex_results.begin(); it_iindex_result != iindex_results.end(); ++ it_iindex_result){
            IIndexRecallResult&  recall_result = *it_iindex_result;
            string realid = recall_result.elem->real_id();
            json::iterator it_item = j.find(realid);
            if(it_item == j.end()){
                json item;
                json record;
                recall_result.elem->eleminfo(record);
                if (record[FORWARD_IDX_ID].get<string>() != realid){
                    spdlog::warn("forward index error!!!, query_id={}, elem_id={}, id={}", record[FORWARD_IDX_ID].get<string>(), realid, recall_result.elem->id);
                }
                item.push_back(record);
                item.push_back(0.0);
                item.push_back(json());
                j[realid] = item;
                it_item = j.find(realid);
            }
            it_item.value()[1] = recall_result.weight;//it_item.value()[1].get<float>() + recall_result.weight + (iindex_results.size() - order ++);

            if (it_item.value()[2].find(iindex_type) == it_item.value()[2].end()) {
                it_item.value()[2][iindex_type] = json();
            }
            //it_item.value()[2][iindex_type].push_back(recall_result.recall_key);
            it_item.value()[2][iindex_type][recall_result.recall_key] = recall_result.weight;
            if (it_item.value()[2].size() > 10){
                spdlog::info("build_json_from_recall, iindex_type={}, recall_key={}, realid={}, {}", iindex_type, recall_result.recall_key, realid, it_item.value().dump());
            }
        }
    }
}

void Application::do_ping(const httplib::Request &req, httplib::Response &res) {
    json json_resp;
    json data;
    data["timestamp"] = TimeUtils::now();
    HttpUtils::compose_response(0, string(""), string(""), data, json_resp);
    res.set_content(json_resp.dump(),"application/json");
}

/*
void Application::do_related_recall(const httplib::Request &req, httplib::Response &res) {
    timeval req_start;
    zeromem(req_start);
    TimeUtils::stat_time_cost(&req_start);
    httplib::Params::const_iterator it_itemid = req.params.find("itemid");
    httplib::Params::const_iterator it_count = req.params.find("count");
    httplib::Params::const_iterator it_repository = req.params.find("repo");


    json json_rsp;
    string content_type = "text/html";
    if (it_itemid == req.params.end() || it_repository == req.params.end() || it_count == req.params.end() ){
        HttpUtils::compose_response(-1, string("param missing"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }
    auto it_recall = _mp_recall_mgr.find(it_repository->second);
    if (it_recall == _mp_recall_mgr.end()){
        HttpUtils::compose_response(-2, string("wrong repo"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }
    RecallProfile profile;
    RecallManager* recall = it_recall->second;
    //get user profile
    int ret = 0;
    if (req.body.size() > 0){
        ret = RecallProfile::decode_from_msgpack(req.body, profile);
        if (ret)
        {
            HttpUtils::compose_response(-3, string("parse profile failed"), string(""), json(), json_rsp);
            res.set_content(json_rsp.dump(),content_type.c_str());
            return;
        }
    }
    //parse strategy
    int count = atoi(it_count->second.data());
    const ElemInfo* src_elem = recall->_fi->get_elem(it_itemid->second);
    if (src_elem == NULL){
        HttpUtils::compose_response(-4, string("no such itemid"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }
    RecallStrategy s;
    if (src_elem->keywords().size() > 0){
        s.recalls["keyword"] = vector<RecallStrategyCell>();
        for(string keyword : src_elem->keywords()){
            RecallStrategyCell c;
            c.key = keyword;
            c.count = count;
            s.recalls["keyword"].push_back(c);
        }
    }

    if (src_elem->category().size() > 0){
        s.recalls["category"] = vector<RecallStrategyCell>();
        for(string category : src_elem->category()){
            RecallStrategyCell c;
            c.key = category;
            c.count = count;
            s.recalls["category"].push_back(c);
        }
    }

    if (src_elem->author() != ""){
        s.recalls["author"] = vector<RecallStrategyCell>();
        RecallStrategyCell c;
        c.key = src_elem->author();
        c.count = count;
        s.recalls["author"].push_back(c);
    }

    if (src_elem->location() != ""){
        s.recalls["location"] = vector<RecallStrategyCell>();
        RecallStrategyCell c;
        c.key = src_elem->author();
        c.count = count;
        s.recalls["location"].push_back(c);
    }

    RecallResult recall_rst;
    recall->recall(&profile, s, recall_rst);
    json data;
    build_json_from_recall(recall_rst, data);
    HttpUtils::compose_response(0, string(""), string(""), data, json_rsp);
    res.set_content(json_rsp.dump(),content_type.c_str());
    uint32_t cost = TimeUtils::stat_time_cost(&req_start);
    spdlog::info("do_related_recall, uid={}, repo={}, cost={}ms", it_itemid->second, it_repository->second, cost);
    //PLOG_INFO << "do_related_recall, uid=" << it_itemid->second << ", repo=" << it_repository->second << ", cost=" << cost << "ms";
    return;
}
*/
void Application::do_get_records_v2(const httplib::Request &req, httplib::Response &res) {
    timeval req_start;
    zeromem(req_start);
    TimeUtils::stat_time_cost(&req_start);
    httplib::Params::const_iterator it_uid = req.params.find("uid");
    httplib::Params::const_iterator it_itemids = req.params.find("itemids");
    httplib::Params::const_iterator it_urls = req.params.find("urls");
    httplib::Params::const_iterator it_repositorys = req.params.find("repos");
    httplib::Params::const_iterator it_full = req.params.find("full");

    bool need_urls = it_urls == req.params.end() || it_urls->second != "0";
    json json_rsp;
    string content_type = "text/html";
    if (it_uid == req.params.end() || it_repositorys == req.params.end() ) {
        HttpUtils::compose_response(-1, string("param missing"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(), content_type.c_str());
        return;
    }

    RecallProfile profile;
    vector<string> itemids;
    spdlog::debug("content length {0:d}", req.body.size());
    //get user profile
    if (it_itemids == req.params.end()){
        //PLOG_INFO << "content length " << req.body.size();
        int ret = RecallProfile::decode_from_msgpack(req.body, profile);
        if (ret)
        {
            HttpUtils::compose_response(-3, string("parse itemids failed"), string(""), json(), json_rsp);
            res.set_content(json_rsp.dump(),content_type.c_str());
            return;
        }
        for(const string& i : profile.filter()){
            itemids.push_back(i);
        }
    }
    else{
        itemids = StringUtils::split(it_itemids->second, ',');
    }


    std::vector<std::string> repos = StringUtils::split(it_repositorys->second, ',');

    json data = {};
    for (std::string& repo : repos){
        auto it_recall = _mp_recall_mgr.find(repo);
        if (it_recall == _mp_recall_mgr.end()){
            spdlog::warn("do_get_records, repo not found, uid={}, repo={}", it_uid->second, repo);
            continue;
        }
        RecallManager* recall = it_recall->second;

        for (auto it_itemid = itemids.begin(); it_itemid != itemids.end(); ){
            string& i = *it_itemid;
            const ElemInfo* e = recall->_fi->get_elem(i);
            if (e != NULL){
                json el;
                if (it_full != req.params.end() && it_full->second == "1"){
                    e->eleminfo(el);
                }else{
                    e->brief_elem(el);
                }
                data.push_back(el);
                it_itemid = itemids.erase(it_itemid);
            }else{
                ++ it_itemid;
            }
        }
    }

    HttpUtils::compose_response(0, string(""), string(""), data, json_rsp);
    res.set_content(json_rsp.dump(),content_type.c_str());
    uint32_t cost = TimeUtils::stat_time_cost(&req_start);
    spdlog::info("\titem_query\t{}\t{}\t{}", it_repositorys->second, profile.count(), cost);
    spdlog::info("do_get_records, uid={}, itemids={}, repo={}, cost={}ms", it_uid->second, itemids.size(), it_repositorys->second, cost);
    //PLOG_INFO << "do_get_records, uid=" << it_uid->second << ", itemids=" << itemids.size() << ", repo=" << it_repository->second << ", cost=" << cost << "ms";
    return;
}

void Application::do_get_records(const httplib::Request &req, httplib::Response &res) {
    timeval req_start;
    zeromem(req_start);
    TimeUtils::stat_time_cost(&req_start);
    httplib::Params::const_iterator it_uid = req.params.find("uid");
    httplib::Params::const_iterator it_repository = req.params.find("repo");
    httplib::Params::const_iterator it_itemids = req.params.find("itemids");
    httplib::Params::const_iterator it_urls = req.params.find("urls");

    bool need_urls = it_urls == req.params.end() || it_urls->second != "0";
    json json_rsp;
    string content_type = "text/html";
    if (it_uid == req.params.end() || it_repository == req.params.end() ){
        HttpUtils::compose_response(-1, string("param missing"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }
    auto it_recall = _mp_recall_mgr.find(it_repository->second);
    if (it_recall == _mp_recall_mgr.end()){
        HttpUtils::compose_response(-2, string("wrong repo"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }
    RecallProfile profile;
    RecallManager* recall = it_recall->second;
    vector<string> itemids;
    spdlog::debug("content length {0:d}", req.body.size());
    //get user profile
    if (it_itemids == req.params.end()){
        //PLOG_INFO << "content length " << req.body.size();
        int ret = RecallProfile::decode_from_msgpack(req.body, profile);
        if (ret)
        {
            HttpUtils::compose_response(-3, string("parse itemids failed"), string(""), json(), json_rsp);
            res.set_content(json_rsp.dump(),content_type.c_str());
            return;
        }
        for(const string& i : profile.filter()){
            itemids.push_back(i);
        }
    }
    else{
        itemids = StringUtils::split(it_itemids->second, ',');
    }


    json data = {};
    for (const string& i : itemids){
        const ElemInfo* e = recall->_fi->get_elem(i);
        if (e == NULL){
            continue;
        }
        json el;
        e->brief_elem(el);
        data.push_back(el);
    }
    HttpUtils::compose_response(0, string(""), string(""), data, json_rsp);
    res.set_content(json_rsp.dump(),content_type.c_str());
    uint32_t cost = TimeUtils::stat_time_cost(&req_start);
    spdlog::info("\titem_query\t{}\t{}\t{}", it_repository->second, profile.count(), cost);
    spdlog::info("do_get_records, uid={}, itemids={}, repo={}, cost={}ms", it_uid->second, itemids.size(), it_repository->second, cost);
    //PLOG_INFO << "do_get_records, uid=" << it_uid->second << ", itemids=" << itemids.size() << ", repo=" << it_repository->second << ", cost=" << cost << "ms";
    return;
}

void Application::do_strategy_recall(const httplib::Request &req, httplib::Response &res) {
    timeval req_start;
    zeromem(req_start);
    TimeUtils::stat_time_cost(&req_start);
    httplib::Params::const_iterator it_uid = req.params.find("uid");
    httplib::Params::const_iterator it_strategy = req.params.find("s");
    httplib::Params::const_iterator it_repository = req.params.find("repo");


    json json_rsp;
    string content_type = "text/html";
    if (it_uid == req.params.end() || it_strategy == req.params.end()|| it_repository == req.params.end() ){
        uint32_t cost = TimeUtils::stat_time_cost(&req_start);
        spdlog::info("\tdo_strategy_recall\t{}\t{}\t{}", it_repository == req.params.end() ? "?" : it_repository->second, -2, cost);
        HttpUtils::compose_response(-1, string("param missing"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }
    auto it_recall = _mp_recall_mgr.find(it_repository->second);
    if (it_recall == _mp_recall_mgr.end()){
        uint32_t cost = TimeUtils::stat_time_cost(&req_start);
        spdlog::info("\tdo_strategy_recall\t{}\t{}\t{}", it_repository->second, -2, cost);
        HttpUtils::compose_response(-2, string("wrong repo"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }
    RecallProfile profile;
    RecallManager* recall = it_recall->second;
    //get user profile
    int ret = RecallProfile::decode_from_msgpack(req.body, profile);
    if (ret)
    {
        uint32_t cost = TimeUtils::stat_time_cost(&req_start);
        spdlog::info("\tdo_strategy_recall\t{}\t{}\t{}", it_repository->second, -3, cost);
        HttpUtils::compose_response(-3, string("parse profile failed"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }
    int strategy_count = 0;
    //parse strategy
    json strategy = json::parse(HttpUtils::url_decode(it_strategy->second));
    RecallStrategy s;
    for(auto it_iindex_type = strategy.begin(); it_iindex_type != strategy.end(); ++ it_iindex_type){
        string iindex_type = it_iindex_type.key();
        s.recalls[iindex_type] = vector<RecallStrategyCell>();
        json& iindex_strategys = it_iindex_type.value();
        for(size_t i = 0; i < iindex_strategys.size(); i ++){
            auto it_key = iindex_strategys[i].find("key");
            auto it_source = iindex_strategys[i].find("source_limit");
            auto it_count = iindex_strategys[i].find("count");
            auto it_key_limits = iindex_strategys[i].find("key_limit");
            if (it_key == iindex_strategys[i].end() || it_source == iindex_strategys[i].end() || it_count == iindex_strategys[i].end()){
                HttpUtils::compose_response(-1, string("invalid strategy"), string(""), json(), json_rsp);
                res.set_content(json_rsp.dump(),content_type.c_str());
                return;
            }
            RecallStrategyCell c;
            c.key = it_key.value().get<string>();
            c.source_limit = it_source.value().get<vector<string>>();
            if (it_key_limits != iindex_strategys[i].end()) {
                const vector<string>& v =  it_key_limits.value().get<vector<string>>();
                //spdlog::info("key={}, keylimits={}", c.key.data(), v.size());
                c.limit_keys = v;
            }
            c.count = it_count.value().get<int>();
            s.recalls[iindex_type].push_back(c);
            strategy_count += it_source->size();
        }
    }

    RecallResult recall_rst;
    int count = recall->recall(&profile, s, recall_rst);
    json data;
    build_json_from_recall(recall_rst, data);
    HttpUtils::compose_response(0, string(""), string(""), data, json_rsp);
    res.set_content(json_rsp.dump(),content_type.c_str());
    uint32_t cost = TimeUtils::stat_time_cost(&req_start);
    spdlog::info("do_strategy_recall, uid={}, repo={}, count={}, cost={}ms", it_uid->second, it_repository->second, count, cost);
    spdlog::info("\tdo_strategy_recall\t{}\t{}\t{}", it_repository->second, ret, cost);
    spdlog::info("\trecall_query\t{}\t{}\t{}", it_repository->second, strategy_count, cost);
    //PLOG_INFO << "do_strategy_recall, uid=" << it_uid->second << ", repo=" << it_repository->second << ", cost=" << cost << "ms";
    return;
}
void Application::do_get_repo_status(const httplib::Request& req, httplib::Response& res){
    timeval req_start;
    zeromem(req_start);
    TimeUtils::stat_time_cost(&req_start);
    json json_rsp;
    json json_data;
    string content_type = "text/html";
    res.set_header("charset", "utf-8");
    for(auto it_recall_mgr = this->_mp_recall_mgr.begin(); it_recall_mgr != this->_mp_recall_mgr.end(); ++ it_recall_mgr){
        const string& repo_key = it_recall_mgr->first;
        json repo_status;
        RecallManager* recall = it_recall_mgr->second;
        recall->get_repository_status(repo_status);
        json_data[repo_key] = repo_status;
    }
    HttpUtils::compose_response(0, string(""), string(""), json_data, json_rsp);
    res.set_content(json_rsp.dump(),content_type.c_str());
    uint32_t cost = TimeUtils::stat_time_cost(&req_start);
    spdlog::info("do_get_repo_status, cost={}ms", cost);
    return;
}
void Application::do_param_recall(const httplib::Request &req, httplib::Response &res) {
    timeval req_start;
    zeromem(req_start);
    TimeUtils::stat_time_cost(&req_start);
    httplib::Params::const_iterator it_type = req.params.find("type");
    httplib::Params::const_iterator it_param = req.params.find("param");
    httplib::Params::const_iterator it_count = req.params.find("count");
    httplib::Params::const_iterator it_repository = req.params.find("repo");
    httplib::Params::const_iterator it_source = req.params.find("source");
    json json_rsp;
    string content_type = "text/html";
    res.set_header("charset", "utf-8");
    if (it_type == req.params.end() || it_param == req.params.end()||it_type == req.params.end()||it_repository == req.params.end()){
        HttpUtils::compose_response(-1, string("param missing"), string(""), json(), json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        return;
    }else{
        int count = atoi(it_count->second.data());
        const string& type = it_type->second;
        const string& repository = it_repository->second;
        auto it_recall_mgr = this->_mp_recall_mgr.find(repository);
        if (it_recall_mgr == this->_mp_recall_mgr.end()){
            HttpUtils::compose_response(-3, string("no such repository"), string(""), json(), json_rsp);
            res.set_content(json_rsp.dump(),content_type.c_str());
            return;
        }
        RecallManager* recall = it_recall_mgr->second;
        vector<string> sources = StringUtils::split(it_source->second, ',');
        RecallProfile profile;
        RecallStrategy s;
        if(type == "category"){
            vector<string> keys = StringUtils::split(it_param->second, ',');
            s.recalls["category"] = vector<RecallStrategyCell>();
            for (string& key : keys) {
                RecallStrategyCell cell;
                cell.key = key;
                cell.count = count;
                cell.source_limit = sources;
                s.recalls["category"].push_back(cell);
            }
        }else if(type == "tops"){
            string key = it_param->second;
            s.recalls["tops"] = vector<RecallStrategyCell>();
            RecallStrategyCell cell;
            cell.key = key;
            cell.count = count;
            cell.source_limit = sources;
            s.recalls["tops"].push_back(cell);
        }else if(type == "location"){
            vector<string> keys = StringUtils::split(it_param->second, ',');
            s.recalls["location"] = vector<RecallStrategyCell>();
            for (string& key : keys) {
                RecallStrategyCell cell;
                cell.key = key;
                cell.count = count;
                cell.source_limit = sources;
                s.recalls["location"].push_back(cell);
            }
        }else if(type == "author"){
            vector<string> keys = StringUtils::split(it_param->second, ',');
            s.recalls["author"] = vector<RecallStrategyCell>();
            for (string& key : keys) {
                RecallStrategyCell cell;
                cell.key = key;
                cell.count = count;
                cell.source_limit = sources;
                s.recalls["author"].push_back(cell);
            }
        }else if(type == "keyword"){
            vector<string> keys = StringUtils::split(it_param->second, ',');
            s.recalls["keyword"] = vector<RecallStrategyCell>();
            unordered_map<string, float> keywords_weight;
            for (string& key : keys){
                string keyword;
                float weight = 0.0;
                vector<string> chunks = StringUtils::split(key, ':');
                if (chunks.size() != 2){
                    HttpUtils::compose_response(-2, string("keyword error"), string(""), json(), json_rsp);
                    res.set_content(json_rsp.dump(),content_type.c_str());
                    return;
                }
                keyword = chunks[0];
                weight = (float)atof(chunks[1].data());
                keywords_weight[keyword] = weight;
                RecallStrategyCell cell;
                cell.key = keyword;
                cell.count = count;
                cell.source_limit = sources;
                s.recalls["keyword"].push_back(cell);
            }


            //this->_w2v->calc_keywords_vector(keywords_weight, profile.keyword_vector);
        }
        RecallResult recall_rst;
        recall->recall(&profile, s, recall_rst);
        json data;
        build_json_from_recall(recall_rst, data);
        HttpUtils::compose_response(0, string(""), string(""), data, json_rsp);
        res.set_content(json_rsp.dump(),content_type.c_str());
        uint32_t cost = TimeUtils::stat_time_cost(&req_start);
        spdlog::info("do_param_recall, count={}, repo={}, cost={}ms", it_count->second, it_repository->second, cost);
        //PLOG_INFO << "do_param_recall, count=" << it_count->second << ", repo=" << it_repository->second << ", cost=" << cost << "ms";
        return;
    }
}
int Application::server_thread(string localip, int port) {
    _svr.Get("/get_repo_status", [this](const httplib::Request& req, httplib::Response& res){
        this->do_get_repo_status(req, res);
        return;
    });
    _svr.Get("/param_recall", [this](const httplib::Request& req, httplib::Response& res){
        this->do_param_recall(req, res);
        return;
    });
    _svr.Get("/strategy_recall", [this](const httplib::Request& req, httplib::Response& res){
        this->do_strategy_recall(req, res);
        return;
    });
    _svr.Post("/strategy_recall", [this](const httplib::Request& req, httplib::Response& res){
        this->do_strategy_recall(req, res);
        return;
    });
    /*
    _svr.Get("/related_recall", [this](const httplib::Request& req, httplib::Response& res){
        this->do_related_recall(req, res);
        return;
    });
    */
    _svr.Get("/get_records", [this](const httplib::Request& req, httplib::Response& res){
        this->do_get_records(req, res);
        return;
    });
    _svr.Post("/get_records", [this](const httplib::Request& req, httplib::Response& res){
        this->do_get_records(req, res);
        return;
    });

    _svr.Get("/get_records_v2", [this](const httplib::Request& req, httplib::Response& res){
        this->do_get_records_v2(req, res);
        return;
    });
    _svr.Post("/get_records_v2", [this](const httplib::Request& req, httplib::Response& res){
        this->do_get_records_v2(req, res);
        return;
    });

    _svr.Post("/ping", [this](const httplib::Request& req, httplib::Response& res){
        this->do_ping(req, res);
        return;
    });
    spdlog::info("starting server thread...");
    if(!_svr.listen(localip.c_str(), port))
    {
        spdlog::error("start server failed");
        return -1;
    }
    return 0;
}

void Application::repository_thread() {
#ifdef SET_AFFINITY
    cpu_set_t mask;
    cpu_set_t get;
    int thread_core_index = 1;
    CPU_ZERO(&mask);
    CPU_SET(thread_core_index, &mask);
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
        spdlog::error("set thread affinity failed");

    }
    CPU_ZERO(&get);
    if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) {
        spdlog::error("get thread affinity failed");
    }
    int num = sysconf(_SC_NPROCESSORS_CONF);
    int cpu_set = -1;
    for(int i = 0; i < num; i ++){
        if (CPU_ISSET(i, &get)) {
            cpu_set = i;
            break;
        }
    }
    printf("repository thread started, scheduled to core %d\n" , cpu_set);
    spdlog::info("repository thread started, scheduled to core {}" , cpu_set);
#endif
    while(_repository_thread_flag){
        reload_repositorys();
        this_thread::sleep_for(milliseconds(2000));
    }
}

int Application::reload_repositorys() {
    int ret = 0;
    for(auto it = _mp_recall_mgr.begin(); it != _mp_recall_mgr.end(); ++ it){
        timeval start;
        gettimeofday(&start, NULL);
        ret = it->second->reload_repository();
        if (ret != 0){
            timeval now;
            gettimeofday(&now, NULL);
            spdlog::info("\trepository_reloaded\t{}\t{}\t{}", it->first, ret, now.tv_sec - start.tv_sec);
        }
        if(ret == 1){
            spdlog::info("repository {} reloaded", it->first);
        }
        else if(ret < 0){
            spdlog::error("repository reload failed, repo={}, ret={}", it->first, ret);
        }
    }
    return 0;
}
int Application::init_log() {
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::debug);
    console_sink->set_pattern("[%Y-%m-%d %H:%M:%S][%t][%L] %v");

    auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt >(LOG_FILE_PATH_SPD, 1024*1024*10, 3);
    file_sink->set_level(spdlog::level::debug);
    file_sink->set_pattern("[%Y-%m-%d %H:%M:%S][%t][%L] %v");
    spdlog::logger logger("multi_sink", {console_sink, file_sink});
    logger.set_level(spdlog::level::debug);

    spdlog::set_default_logger(std::make_shared<spdlog::logger>(logger));

    return 0;
}
int Application::init() {
    int ret = 0;

    init_log();
    /*
    plog::RollingFileAppender<plog::TxtFormatter> rolling(LOG_FILE_PATH, 1024*1024*10, 3);
    plog::ConsoleAppender<plog::TxtFormatter> c;

    plog::init(plog::debug, &rolling).addAppender(&c);
    */
    ifstream json_file(CFG_FILE_PATH);
    if (json_file.is_open()) {
        json_file >> _cfg;
        //word 2 vec
        //_w2v = new WordVectorLib(_cfg["word_vector_path"].get<string>());
        //recall models
        string localip = "localhost";
        string host = "eth0";
        json::iterator it_eth = _cfg.find("network");
        if (it_eth != _cfg.end()){
            host = it_eth.value().get<string>();
            spdlog::info("network={}", host);
        }
#ifdef linux
        if (HttpUtils::get_host_ip(host, localip)){
        PLOG_ERROR << "failed get local ip";
        return -1;
    }

#endif
        string file_lock = _cfg["lock"].get<string>();
        int lock = FileUtils::lock_file(file_lock.c_str());
        if (lock < 0){
            spdlog::info("lock failed, lock={}, ret={}", file_lock, lock);
            return -1;
        }
        spdlog::info("local ip={}", localip);
        int reload_delay = 0;
        json::iterator it_reload_delay = _cfg.find("reload_delay");
        if (it_reload_delay != _cfg.end()){
            reload_delay = it_reload_delay.value().get<int>();
        }
        for(json::iterator it_rm = _cfg["recall_repositorys"].begin(); it_rm != _cfg["recall_repositorys"].end(); ++ it_rm){
            const string& key = it_rm.key();
            json& repository = it_rm.value();
            if (_mp_recall_mgr.find(key) == _mp_recall_mgr.end()){
                timeval start;
                gettimeofday(&start, NULL);
                spdlog::info("loading repository {}...", key);
                map<string, string> invert_indexes;
                for(auto it_iindex = repository.begin(); it_iindex != repository.end(); ++ it_iindex){
                    const string& iindex_key = it_iindex.key();
                    if(iindex_key.find("iindex") == iindex_key.npos){
                        continue;
                    }
                    string path = it_iindex.value().get<string>();
                    invert_indexes[iindex_key] = path;
                }
                RecallManager* mgr = new RecallManager(_w2v, repository["forward_index"].get<string>(), invert_indexes, reload_delay);
                ret = mgr->init();
                timeval now;
                gettimeofday(&now, NULL);
                spdlog::info("\trepository_reloaded\t{}\t{}\t{}", key, ret, now.tv_sec - start.tv_sec);
                if (ret <0){
                    spdlog::error("load repository failed, ret={}", ret);
                    //PLOG_ERROR << "load repository failed" ;
                }
                _mp_recall_mgr[key] = mgr;
            }
        }
        //load keywords
        timeval start;
        gettimeofday(&start, NULL);
        unordered_map<string, int> w2v_keywords;
        for(auto it = _mp_recall_mgr.begin(); it != _mp_recall_mgr.end(); ++ it){
            it->second->stat_keywords(w2v_keywords);
        }
        int core_num = sysconf(_SC_NPROCESSORS_CONF);
        spdlog::info("system cores={}", core_num);
        /*
        PLOG_INFO << "load word vectors, keywords="  << w2v_keywords.size();
        ret = _w2v->load_vectors(w2v_keywords);
        if(ret)
        {
            PLOG_ERROR << "load word vector failed, ret=" << ret;
        } else{
            timeval now;
            gettimeofday(&now, NULL);
            PLOG_INFO << "load done, cost=" << now.tv_sec - start.tv_sec << "s";
        }
        */
        //start repository thread
        _repository_thread = new thread(&Application::repository_thread, this);
        //start server
        _server_thread = new thread(&Application::server_thread, this, localip, _cfg["port"].get<int>());

        spdlog::info("load config finish...");
        return 0;

    } else{
        spdlog::error("load config file failed, log={}", CFG_FILE_PATH);
        return -1;
    }
}
int run_flag = 1;

int main(int argc, const char** argv){
    int ret = 0;
    Application app;
    ret = app.init();
    //ret = 0;
    if (ret){
        return -1;
    }
    while(run_flag){
        usleep(1000 );
    }
    return 0;
}